﻿// <file>
//     <copyright see="prj:///doc/copyright.txt"/>
//     <license see="prj:///doc/license.txt"/>
//     <owner name="Mike Krüger" email="mike@icsharpcode.net"/>
//     <version>$Revision$</version>
// </file>

using System;
using System.Drawing;
using System.Windows.Forms;

namespace ICSharpCode.TextEditor.Gui.CompletionWindow
{
	/// <summary>
	/// Description of CodeCompletionListView.
	/// </summary>
	public class CodeCompletionListView : System.Windows.Forms.UserControl
	{
		
		int               firstItem    = 0;
		int               selectedItem = -1;
        Color             borderColor = SystemColors.ControlLight;
        string            theme = "Default";

        ImageList imageList;

        ICompletionData[] completionData;

        public ImageList ImageList {
			get {
				return imageList;
			}
			set {
				imageList = value;
			}
		}
		
		public int FirstItem {
			get {
				return firstItem;
			}
			set {
				if (firstItem != value) {
					firstItem = value;
					OnFirstItemChanged(EventArgs.Empty);
				}
			}
		}

        public ICompletionData[] CompletionData
        {
            get { return completionData; }
            set
            {
                completionData = value;
                Array.Sort(completionData, DefaultCompletionData.Compare);
                this.Invalidate();
            }
        }

        public ICompletionData SelectedCompletionData {
			get {
				if (selectedItem < 0) {
					return null;
				}
				return completionData[selectedItem];
			}
		}
		
		public int ItemHeight {
			get {
				return Math.Max(imageList.ImageSize.Height, (int)(Font.Height * 1.25));
			}
		}
		
		public int MaxVisibleItem {
			get {
				return Height / ItemHeight;
			}
		}
		
        public string Theme
        {
            get { return theme; }
            set
            {
                theme = value;
                DoThemeChanged(new EventArgs());
                this.Invalidate();
            }
        }

		public CodeCompletionListView(ICompletionData[] completionData)
		{
            InitializeControls();
            CompletionData = completionData;           
            //if (completionData != null && completionData.Length > 0)
            //    SelectIndex(0);
        }

        public void InitializeControls()
        {
            this.Font = new Font("宋体", 10.0f);
            this.BorderStyle = BorderStyle.None;
        }
		
        public void DoThemeChanged(EventArgs e)
        {
            switch (this.theme)
            {
                case "Default":                    
                    CompletionItemBrushes.Highlight = SystemBrushes.Highlight;
                    CompletionItemBrushes.HighlightText= SystemBrushes.HighlightText;
                    CompletionItemBrushes.Window = SystemBrushes.Window;
                    CompletionItemBrushes.WindowText = SystemBrushes.WindowText;
                    borderColor = SystemColors.ControlLight;
                    break;
                case "Black":
                    CompletionItemBrushes.Highlight = new SolidBrush(Color.FromArgb(000, 122, 204));
                    CompletionItemBrushes.HighlightText = new SolidBrush(Color.FromArgb(255, 255, 255));
                    CompletionItemBrushes.Window = new SolidBrush(Color.FromArgb(030, 030, 030));
                    CompletionItemBrushes.WindowText = new SolidBrush(Color.FromArgb(216, 218, 219));
                    borderColor = Color.FromArgb(000, 122, 204);
                    break;
            }
            System.Console.WriteLine("CodeCompletionListView：改变了主题" + theme);
        }

		public void Close()
		{
			if (completionData != null) {
				Array.Clear(completionData, 0, completionData.Length);
			}
			base.Dispose();
		}
		
		public void SelectIndex(int index)
		{
			int oldSelectedItem = selectedItem;
			int oldFirstItem    = firstItem;
			
			index = Math.Max(0, index);
			selectedItem = Math.Max(0, Math.Min(completionData.Length - 1, index));
			if (selectedItem < firstItem) {
				FirstItem = selectedItem;
			}
			if (firstItem + MaxVisibleItem <= selectedItem) {
				FirstItem = selectedItem - MaxVisibleItem + 1;
			}
			if (oldSelectedItem != selectedItem) {
				if (firstItem != oldFirstItem) {
					Invalidate();
				} else {
					int min = Math.Min(selectedItem, oldSelectedItem) - firstItem;
					int max = Math.Max(selectedItem, oldSelectedItem) - firstItem;
					Invalidate(new Rectangle(0, 1 + min * ItemHeight, Width, (max - min + 1) * ItemHeight));
				}
				OnSelectedItemChanged(EventArgs.Empty);
			}
		}
		
		public void CenterViewOn(int index)
		{
			int oldFirstItem = this.FirstItem;
			int firstItem = index - MaxVisibleItem / 2;
			if (firstItem < 0)
				this.FirstItem = 0;
			else if (firstItem >= completionData.Length - MaxVisibleItem)
				this.FirstItem = completionData.Length - MaxVisibleItem;
			else
				this.FirstItem = firstItem;
			if (this.FirstItem != oldFirstItem) {
				Invalidate();
			}
		}
		
		public void ClearSelection()
		{
			if (selectedItem < 0)
				return;
			int itemNum = selectedItem - firstItem;
			selectedItem = -1;
			Invalidate(new Rectangle(0, itemNum * ItemHeight, Width, (itemNum + 1) * ItemHeight + 1));
			Update();
			OnSelectedItemChanged(EventArgs.Empty);
		}
		
		public void PageDown()
		{
			SelectIndex(selectedItem + MaxVisibleItem);
		}
		
		public void PageUp()
		{
			SelectIndex(selectedItem - MaxVisibleItem);
		}
		
		public void SelectNextItem()
		{
			SelectIndex(selectedItem + 1);
		}
		
		public void SelectPrevItem()
		{
			SelectIndex(selectedItem - 1);
		}

        public void SelectItemWithStart(string startText)
        {
            if (startText == null || startText.Length == 0) return;
            string originalStartText = startText;
            startText = startText.ToLower();
            int bestIndex = -1;
            int bestQuality = -1;
            // Qualities: 0 = match start
            //            1 = match start case sensitive
            //            2 = full match
            //            3 = full match case sensitive
            double bestPriority = 0;
            for (int i = 0; i < completionData.Length; ++i)
            {
                string itemText = completionData[i].Text;
                string lowerText = itemText.ToLower();
                if (lowerText.StartsWith(startText))
                {
                    double priority = completionData[i].Priority;
                    int quality;
                    if (lowerText == startText)
                    {
                        if (itemText == originalStartText)
                            quality = 3;
                        else
                            quality = 2;
                    }
                    else if (itemText.StartsWith(originalStartText))
                    {
                        quality = 1;
                    }
                    else
                    {
                        quality = 0;
                    }
                    bool useThisItem;
                    if (bestQuality < quality)
                    {
                        useThisItem = true;
                    }
                    else
                    {
                        if (bestIndex == selectedItem)
                        {
                            useThisItem = false;
                        }
                        else if (i == selectedItem)
                        {
                            useThisItem = bestQuality == quality;
                        }
                        else
                        {
                            useThisItem = bestQuality == quality && bestPriority < priority;
                        }
                    }
                    if (useThisItem)
                    {
                        bestIndex = i;
                        bestPriority = priority;
                        bestQuality = quality;
                    }
                }
            }
            if (bestIndex < 0)
            {
                ClearSelection();
            }
            else
            {
                if (bestIndex < firstItem || firstItem + MaxVisibleItem <= bestIndex)
                {
                    SelectIndex(bestIndex);
                    CenterViewOn(bestIndex);
                }
                else
                {
                    SelectIndex(bestIndex);
                }
            }
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            float yPos = 1;
            float itemHeight = ItemHeight;
            // Maintain aspect ratio
            int imageWidth = (int)(itemHeight * imageList.ImageSize.Width / imageList.ImageSize.Height);

            int curItem = firstItem;
            Graphics g = pe.Graphics;
            while (curItem < completionData.Length && yPos < Height)
            {
                RectangleF drawingBackground = new RectangleF(1, yPos, Width - 2, itemHeight);
                if (drawingBackground.IntersectsWith(pe.ClipRectangle))
                {
                    // draw Background
                    if (curItem == selectedItem)
                    {
                        g.FillRectangle(CompletionItemBrushes.Highlight, drawingBackground);
                    }
                    else
                    {
                        g.FillRectangle(CompletionItemBrushes.Window, drawingBackground);
                    }

                    // draw Icon
                    int xPos = 0;
                    if (imageList != null && completionData[curItem].ImageIndex < imageList.Images.Count)
                    {
                        RectangleF iconRectangleF = new RectangleF(1 + (imageWidth - 16) / 2, yPos + (itemHeight - 16) / 2, 16, 16);
                        g.DrawImage(imageList.Images[completionData[curItem].ImageIndex], iconRectangleF);
                        xPos = imageWidth;
                    }
                    // draw text
                    if (curItem == selectedItem)
                    {
                        g.DrawString(completionData[curItem].Text, Font, CompletionItemBrushes.HighlightText, xPos, yPos + 3);
                    }
                    else
                    {
                        g.DrawString(completionData[curItem].Text, Font, CompletionItemBrushes.WindowText, xPos, yPos + 3);
                    }
                }

                yPos += itemHeight;
                ++curItem;
            }
            g.DrawRectangle(new Pen(borderColor, 1.0f), new Rectangle(0, 0, Width - 1, Height - 1));
        }
		
		protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
		{
			float yPos       = 1;
			int curItem = firstItem;
			float itemHeight = ItemHeight;
			
			while (curItem < completionData.Length && yPos < Height) {
				RectangleF drawingBackground = new RectangleF(1, yPos, Width - 2, itemHeight);
				if (drawingBackground.Contains(e.X, e.Y)) {
					SelectIndex(curItem);
					break;
				}
				yPos += itemHeight;
				++curItem;
			}
		}
		
		protected override void OnPaintBackground(PaintEventArgs pe)
		{
		}
		
		protected virtual void OnSelectedItemChanged(EventArgs e)
		{
			if (SelectedItemChanged != null) {
				SelectedItemChanged(this, e);
			}
		}
		
		protected virtual void OnFirstItemChanged(EventArgs e)
		{
			if (FirstItemChanged != null) {
				FirstItemChanged(this, e);
			}
		}        

        protected override bool ProcessDialogKey(Keys keyData)
        {
            switch (keyData)
            {
                case Keys.Home:
                    SelectIndex(0);
                    return true;
                case Keys.End:
                    SelectIndex(completionData.Length - 1);
                    return true;
                case Keys.PageDown:
                    PageDown();
                    return true;
                case Keys.PageUp:
                    PageUp();
                    return true;
                case Keys.Down:
                    SelectNextItem();
                    return true;
                case Keys.Up:
                    SelectPrevItem();
                    return true;               
            }
            return base.ProcessDialogKey(keyData);
        }

        public event EventHandler SelectedItemChanged;
		public event EventHandler FirstItemChanged;
	}
}
